# -*- coding: utf-8 -*-
'''
Created on 2009/05/28

@author: coLun
'''

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext.webapp.util import login_required
import os
from html import *
from model import *

class TopPage(HtmlViewBase):
    path = '/'
    menu_name = 'TOP'
    menu_path = '/'
    @classmethod
    def getMenu(cls):
        return (cls.menu_name, cls.menu_path)
    def get(self):
        self.printHeader(u'高専プロコン2009競技練習場', [
                ProfilePage.getMenu(),
                MakeProblemPage.getMenu(),
				self.getEditorMenu(),
                HelpPage.getMenu(),
                self.getSupportMenu() ])
        self.response.out.write(
u'''<div>
ご連絡：<br />
<font color="red">★</font><a href="http://pc-2009.appspot.com/editor/">競技問題エディタ</a>を作りました。問題作成に、ご活用下さい。　→　[<a href="http://pc-2009.colun.net/200908/09080001.html">詳しくはこちら</a>] (2009/08/24)<br />
<font color="red">★</font>各問題ごとに雑談機能が追加されました。<br />
　　ただし、問題の具体的解き方（アルゴリズム）に関する話題はネタバレになる場合もあるので、<br />
　　あんまり直接的なのは避ける方向でお願いします。 (2009/06/28)<br />
<font color="red">★</font>試合時csvファイル取得時に、GoogleAccountログインを不要にしました。<br />
　　（これで、csv自動DLプログラムによるロケットスタートが可能になります） (2009/06/24)<br />
</div>
'''
                )
        self.response.out.write('<div><table border="1">')
        self.response.out.write(u'<tr><td>問題名</td><td>首位</td><td>更新日付</td></tr>')
        q = ProblemSet.gql('ORDER BY CDate ASC')
        pool = EntityPool()
        for p in q:
            pool.add(p.get_key('holder'))
        for p in q:
            subject = p.subject
            if len(subject)==0:
                subject = '(NON TITLE)'
            holder_entity = pool[p.get_key('holder')]
            if holder_entity==None:
                holder = '---'
            else:
                holder = holder_entity.getHtml()
            self.response.out.write('<tr><td><a href="%s">%s</a></td><td>%s</td><td>%s</td></tr>' % (ProblemIndexPage.getPath(p.problemId), subject, holder, (p.LDate + timedelta(seconds = 9 * 3600)).strftime('%Y/%m/%d %H:%M')))
        self.response.out.write('</table></div>')
        self.printFooter()

class HelpPage(HtmlViewBase):
    path = '/help\\.html'
    menu_name = u'取り扱い説明書'
    menu_path = '/help.html'
    @classmethod
    def getMenu(cls):
        return (cls.menu_name, cls.menu_path)
    def get(self):
        self.printHeader(u'取り扱い説明書 - 高専プロコン2009競技練習場', [
                TopPage.getMenu(),
                self.getSupportMenu() ])
        self.response.out.write(
u'''<pre>■ご挨拶

このアプリケーションは、高専プロコン2009競技部門のために作られました。

競技部門に参加する現役高専生、未だ競技部門に熱い情熱をそそぐ高専OB/OGの方々、
高専プロコンに興味のある方々、高専関係ないけど、プログラミングは大好きですという方々、
そんなあなた方のために、このアプリケーションは作られました。

どうぞ使ってください。

■ひとり言（作成動機）（日記からの転載）

とりあえず７月の時点で一応動く練習サーバが一般に知れ渡っていれば、試合までに切磋琢磨できるんじゃないかと。
互いの実力が分からない状態で対峙して、一回こっきりの勝負で使い捨てられるプログラムじゃ、つまんないし。
負けて、改良して勝つ所に、プロコン競技部門の面白さがある。
それを現地でしかできないのは、もったいない。
ましてや去年みたいなのは、かわいそすぎる。

■使い方

テキトーに使ってください。

ログインはGoogleアカウントを使って行います。
ログイン後、ランキングのためにプロフィールを編集しておくことをおすすめします。

問題csvは、試合画面に入らないとDLできません。
試合画面にはグラフィカルな確認モードが付いていますが、
これは競技プログラムの開発段階用の機能です。
本番練習をする際には使わないことをオススメします。

問題は自由に追加してください。
<s>現在、ランダム作成機能しかついていません。</s>
<s>フィールドのサイズと、ブロックの色数を指定できます。</s>

ランダム作成機能では、フィールドサイズとブロックの色数を指定するだけで、
お手軽に問題を作成できます。

また、競技問題エディタを使えば、非ランダムに自在に問題を編集可能です。
完成した問題csvを使って、csvファイルをアップロードして問題を作成可能です。

とにかく使ってみてください。
どんどん、試合をこなしていってみてください。

■ルールについて

全国高等専門学校プログラミングコンテスト
<a href="http://www.procon.gr.jp/">http://www.procon.gr.jp/</a>

サーバ仕様は、上記サイトの競技部門のルール説明を元に作られています。

■提出csvについて

判定を少しきつくしています。
改行コードはCR LFしか認めていません。
カンマ区切りなのですが、数字の前とか後とかに余計な空白があっても、
エラーが出るように、わざとしています。
これは、本番サーバーの仕様が正確にわかっていないので、
あえて厳しくしています。

■雑談機能について

交流目的で、雑談機能が付いています。
問題の具体的解き方（アルゴリズム）に関する話題はネタバレになる場合もあるので、
あんまり直接的なのは避ける方向でお願いします。
＃　ただし、公開されているソースコードを使って、
＃　練習サーバを学内向けに設置した場合など、
＃　避ける必要がない場合もあるかと思います。
＃　その辺は、臨機応変にお願いします。

■ソースコードについて

ソースは以下に公開しています。

training-server-pc2009nct-japan - Google Code
<a href="http://code.google.com/p/training-server-pc2009nct-japan/">http://code.google.com/p/training-server-pc2009nct-japan/</a>

このアプリケーションはGoogle App Engine上で動いています。
Google App Engineの説明は、下記サイトを読んでください。

Google App Engine
<a href="http://code.google.com/appengine/">http://code.google.com/appengine/</a>

Google App Engine上で動くので、app.yamlにあるデプロイ先を変えてあげれば、
個人向けの練習サーバーを簡単に立てることができます。
個人利用程度の負荷なら、Google App Engineは無料の枠内でしっかり動作すると思います。

■言い訳

とりあえず早く使える状態になることを優先して作ったため、
プログラムの各所に不備があります。
気づいているものもあれば、気づいていないものまであると思います。

気づいているものの代表格としては、
ユーザ入力文字列をエンコードせずにそのまま出力しているので、
タグとか自由に入れれてしまったりします。
また、文字数制限も特に指定していなかったりします。
一部のhttp出力ヘッダに、キャラセットutf-8を指定していなかったりもします。

あと、デザインもテキトーにしか作ってません。

というか、どの道、大会当日までしか使わないのだから、
デザインや、試合内容と関係のない細かい所にこだわっても
仕方がないのかもしれないですね。

不具合等ありましたら、<a href="http://pc-2009.colun.net/">サポートラウンジ</a>まで、ご連絡ください。

以上。


コルン＠久留米OB
</pre>
''')
        self.printFooter()

class MakeProblemPage(HtmlViewBase):
    menu_name = u'問題作成'
    menu_path = '/make'
    path = '/make'
    @classmethod
    def getMenu(cls):
        return (cls.menu_name, cls.menu_path)
    @login_required
    def get(self):
        self.printHeader(u'問題作成', [TopPage.getMenu(), self.getEditorMenu()])
        self.response.out.write(
u'''<form method="POST" action="%s">
<div><font color="#A00000"><b>ランダム問題作成</b></font></div>
<div><table>
<tr><td colspan="2">問題名：</td><td colspan="2"><input type="text" name="subject" value="" size="50" /></td></tr>
<tr><td>問A：</td><td>大きさ</td><td><input type="text" name="size1" value="5" size="5" /></td><td>(5～20)</td></tr>
<tr><td></td><td>色</td><td><input type="text" name="color1" value="4" size="5" /></td><td>(2～8)</td></tr>
<tr><td>問B：</td><td>大きさ</td><td><input type="text" name="size2" value="5" size="5" /></td><td>(5～20)</td></tr>
<tr><td></td><td>色</td><td><input type="text" name="color2" value="4" size="5" /></td><td>(2～8)</td></tr>
<tr><td>問C：</td><td>大きさ</td><td><input type="text" name="size3" value="5" size="5" /></td><td>(5～20)</td></tr>
<tr><td></td><td>色</td><td><input type="text" name="color3" value="4" size="5" /></td><td>(2～8)</td></tr>
</table></div>
<div>コメント：</div>
<div><textarea name="comment" cols="60" rows="5"></textarea></div>
<div><input type="submit" value="作成" /></div>
</form>
<hr />
<form enctype="multipart/form-data" method="POST" action="%s">
<div><font color="#A00000"><b>csvファイルをアップロードして問題作成</b></font></div>
<div><table>
<tr><td>問題名：</td><td><input type="text" name="subject" value="" size="50" /></td></tr>
<tr><td>問A：</td><td><input type="file" name="csv_file1" /></td></tr>
<tr><td>問B：</td><td><input type="file" name="csv_file2" /></td></tr>
<tr><td>問C：</td><td><input type="file" name="csv_file3" /></td></tr>
</table></div>
<div>コメント：</div>
<div><textarea name="comment" cols="60" rows="5"></textarea></div>
<div><input type="submit" value="作成" /></div>
</form>
'''
            % (RandomMakeProblemPost.getPath(), FileMakeProblemPost.getPath()))
        self.printFooter()

class RandomMakeProblemPost(webapp.RequestHandler):
    path = '/make_random_post'
    @classmethod
    def getPath(cls):
        return cls.path
    def post(self):
        user = User.getNowUser()
        if user!=None:
            subject = self.request.get("subject")
            csv_array = []
            for i in range(3):
                s = self.request.get("size%d" % (i+1))
                c = self.request.get("color%d" % (i+1))
                csv = ProblemMaker.MakeCsv(int(s), int(c))
                csv_array.append(csv)
            comment = self.request.get("comment")
            id = ProblemSet.New(subject, csv_array, user, comment)
            self.redirect(ProblemIndexPage.getPath(id))
        else:
            self.redirect(TopPage.path)

class FileMakeProblemPost(webapp.RequestHandler):
    path = '/make_file_post'
    @classmethod
    def getPath(cls):
        return cls.path
    def post(self):
        user = User.getNowUser()
        if user!=None:
            subject = self.request.get("subject")
            csv_array = []
            for i in range(3):
                csv = self.request.get("csv_file%d" % (i+1))
                problem = Trans.CsvToProblem(csv)
                csv_array.append(csv)
            comment = self.request.get("comment")
            id = ProblemSet.New(subject, csv_array, user, comment)
            self.redirect(ProblemIndexPage.getPath(id))
        else:
            self.redirect(TopPage.path)

class ProfilePage(HtmlViewBase):
    path = '/profile'
    menu_name = u'プロフィール編集'
    menu_path = '/profile'
    @classmethod
    def getMenu(cls):
        return (cls.menu_name, cls.menu_path)
    @login_required
    def get(self):
        self.printHeader(u"プロフィール修正", [TopPage.getMenu()])
        user = User.getNowUser()
        if user!=None:
            self.response.out.write(
u'''<form method="POST" action="%s">
<div>お名前：　<input type="text" name="nickname" value="%s"></div>
<div>学校：　<input type="text" name="school" value="%s"></div>
<div><input type="submit" value="変更"></div>
</form>
'''
                % (ProfilePost.path, user.getEncNickname(), user.getEncSchool()))
        else:
            EntityCounter.inc("Error_login_required", default = 0)
            self.response.out.write(
u'''<pre>
たぶんGoogle側の不具合だと思っているのですが、
せっかくログインしてもらったのに、こちらにログイン情報が届いていません。
（届くのに、０．１～０．５秒ぐらい時間差があるのだと思っています）

お手数ですが、いったんトップに戻って、もう一度プロフィール修正ページに移動してみてください。
</pre>
'''
                )
        self.printFooter()

class ProfilePost(webapp.RequestHandler):
    path = '/profile_post'
    def post(self):
        guser = users.get_current_user()
        if guser!=None:
            nickname = self.request.get("nickname")
            school = self.request.get("school")
            email = guser.email()
            User.setUserProfile(email, nickname, school)
        self.redirect(ProfilePage.path)

class ProblemIndexPage(HtmlViewBase):
    path = '/problem([1-9][0-9]*)/'
    @staticmethod
    def getPath(problemId):
        return '/problem' + str(problemId) + '/'
    @classmethod
    def getMenu(cls, problemId):
        return (u'問題TOP', cls.getPath(problemId))
    def get(self, problemIdStr):
        problemId = int(problemIdStr)
        problemSet = ProblemSet.Get(problemId)
        if problemSet!=None:
            page = 0
            pageStr = ''
            self.printHeader(problemSet.subject, [TopPage.getMenu()])
            self.response.out.write(
u'''<div>%s</div>
'''
                % (problemSet.comment))
            pool = EntityPool()
            comments = ProblemComment.GetNewlyComments(problemSet)
            for c in comments:
                pool.add(c.get_key('user'))
            ranking = problemSet.getRanking()
            if ranking:
                count = problemSet.getProblemCsvCount()
                alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                self.response.out.write('<p><table border="1">')
                self.response.out.write(u'<tr><td>順位</td><td>名前</td>')
                for i in range(count):
                    self.response.out.write(u'<td>問題%s</td>' % (alpha[i]))
                self.response.out.write(u'<td>更新日付</td>')
                self.response.out.write('</tr>')
                rnk = 0
                k = 1
                my_cmp = RankingEntry.RankingCmp(count)
                b = None
                for entry in ranking:
                    pool.add(entry.get_key('user'))
                for entry in ranking:
                    if b==None or my_cmp(b, entry)!=0:
                        rnk += k
                        k = 1
                    else:
                        k += 1
                    b = entry
                    entryUserName = pool[entry.get_key('user')].getHtml()
                    self.response.out.write(u'<tr><td>%d位(%d)</td><td>%s</td>' % (rnk, entry.rank[count], entryUserName))
                    for i in range(count):
                        self.response.out.write(u'<td>%d位(%d点, %d手, %s)</td>' % (entry.rank[i], entry.scoreList[i], entry.opeCountList[i], self.secToTimeStr(entry.secList[i])))
                    self.response.out.write(u'<td>%s</td>' % ((entry.LDate + timedelta(seconds = 9 * 3600)).strftime('%Y/%m/%d %H:%M')))
                    self.response.out.write('</tr>')
                    
                self.response.out.write('</table></p>')
            else:
                self.response.out.write(u'<p>ランキングデータなし</p>')
            self.response.out.write(
u'''
<p><form action="%s" method="GET">
<input type="submit" value="この問題を解く！" />
</form></p>
'''
                % (GameLobbyPage.getPath(problemId)))
            self.response.out.write('<hr /><p>')
            self.response.out.write(u'<div><font color="#A00000"><b>この問題に対しての雑談</b></font></div>')
            if 0<len(comments):
                commentCount = ProblemComment.GetCommentCount(problemSet)
                pageCount = (commentCount + 99) / 100
                if pageCount:
                    self.response.out.write('<div>page:')
                    for i in range(pageCount):
                        p = i + 1
                        self.response.out.write(' <a href="%s">%d</a>' % (ProblemCommentsViewPage.getPath(problemId, p), p))
                    self.response.out.write('</div>')
                tz = timedelta(seconds = 9 * 3600)
                for c in comments:
                    userName = pool[c.get_key('user')].getHtml()
                    comment = c.comment
                    date = c.CDate + tz
                    self.response.out.write(u'<div>%s： %s(%s)</div>' % (userName, comment, date.strftime('%Y-%m-%d %H:%M')))
                if pageCount:
                    self.response.out.write('<div>page:')
                    for i in range(pageCount):
                        p = i + 1
                        self.response.out.write(' <a href="%s">%d</a>' % (ProblemCommentsViewPage.getPath(problemId, p), p))
                    self.response.out.write('</div>')
                self.response.out.write('</p>')
            else:
                self.response.out.write(u'<p>雑談発言なし</p>')

            user = User.getNowUser()
            self.response.out.write('<p>')
            self.response.out.write(u'<div><font color="#A00000"><b>発言する</b></font></div>')
            if user!=None:
                self.response.out.write('<form method="POST" action="%s">' % (ProblemCommentPost.getPath(problemId)))
                self.response.out.write(u'<div>%s： <input name="comment" size="40" value="" /> <input type="submit" value="発言する" /></div>' % (user.getHtml()))
                self.response.out.write('</form>')
            else:
                self.response.out.write(u'<div>発言するには、ログインが必要です。</div>')
            self.response.out.write('</p>')

            self.response.out.write(
u'''<hr>
<p>
<div><font color="#A00000"><b>カンニング機能（おまけ）</b></font></div>
'''
                )
            for i in range(problemSet.getProblemCsvCount()):
                coId = i + 1
                alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                self.response.out.write(u'<div>問題%s　[<a href="%s">csv</a>] [<a href="%s">html</a>]</div>' % (alpha[i], ProblemCsvViewer.getPath(problemId, coId), ProblemVisualPage.getPath(problemId, coId)))
            self.response.out.write('</p>')
            self.printFooter()
        else:
            self.printHeader('ERROR', [TopPage.getMenu()])
            self.response.out.write(u'<div>問題が見つかりませんでした</div>')
            self.printFooter()

class ProblemCommentsViewPage(HtmlViewBase):
    path = '/problem([1-9][0-9]*)/comments([1-9][0-9]*)'
    @staticmethod
    def getPath(problemId, pageId):
        return '/problem' + str(problemId) + '/comments' + str(pageId)
    def get(self, problemIdStr, pageStr):
        problemId = int(problemIdStr)
        page = int(pageStr)
        problemSet = ProblemSet.Get(problemId)
        if problemSet!=None:
            comments = ProblemComment.GetCommentsByPage(problemSet, page)
            if 0<len(comments):
                pool = EntityPool()
                for c in comments:
                    pool.add(c.get_key('user'))
                self.printHeader(u'コメント(page' + pageStr + ') - ' + problemSet.subject, [TopPage.getMenu(), ProblemIndexPage.getMenu(problemId)])
                self.response.out.write('<p>')
                commentCount = ProblemComment.GetCommentCount(problemSet)
                pageCount = (commentCount + 99) / 100
                if pageCount:
                    self.response.out.write('<div>page:')
                    for i in range(pageCount):
                        p = i + 1
                        if p!=page:
                            self.response.out.write(' <a href="%s">%d</a>' % (ProblemCommentsViewPage.getPath(problemId, p), p))
                        else:
                            self.response.out.write(' %d' % (p))
                    self.response.out.write('</div>')
                tz = timedelta(seconds = 9 * 3600)
                for c in comments:
                    userName = pool[c.get_key('user')].getHtml()
                    comment = c.comment
                    date = c.CDate + tz
                    self.response.out.write(u'<div>%s： %s(%s)</div>' % (userName, comment, date.strftime('%Y-%m-%d %H:%M')))
                if pageCount:
                    self.response.out.write('<div>page:')
                    for i in range(pageCount):
                        p = i + 1
                        if p!=page:
                            self.response.out.write(' <a href="%s">%d</a>' % (ProblemCommentsViewPage.getPath(problemId, p), p))
                        else:
                            self.response.out.write(' %d' % (p))
                    self.response.out.write('</div>')
                self.response.out.write('</p>')
                self.printFooter()
        else:
            self.printHeader('ERROR', [TopPage.getMenu()])
            self.response.out.write(u'<div>問題が見つかりませんでした</div>')
            self.printFooter()

class ProblemCommentPost(webapp.RequestHandler):
    path = '/problem([1-9][0-9]*)/comment-say'
    @staticmethod
    def getPath(problemId):
        return '/problem' + str(problemId) + '/comment-say'
    def post(self, problemIdStr):
        problemId = int(problemIdStr)
        problemSet = ProblemSet.Get(problemId)
        if problemSet!=None:
            comment = self.request.get("comment")
            user = User.getNowUser()
            if user!=None:
                ProblemComment.say(problemSet, user, comment)
            self.redirect(ProblemIndexPage.getPath(problemId))

class ProblemCsvViewer(webapp.RequestHandler):
    path = '/problem([1-9][0-9]*)-([1-9][0-9]*)\\.csv'
    @staticmethod
    def getFullPath(problemId, coId):
        return 'http://' + os.environ["HTTP_HOST"] + '/problem' + str(problemId) + '-' + str(coId) + '.csv'
    @staticmethod
    def getPath(problemId, coId):
        return '/problem' + str(problemId) + '-' + str(coId) + '.csv'
    def get(self, problemIdStr, coIdStr):
        problemId = int(problemIdStr)
        coId = int(coIdStr)
        problemSet = ProblemSet.Get(problemId)
        if problemSet!=None and 1<=coId and coId<=problemSet.getProblemCsvCount():
            self.response.headers['Content-Type'] = 'application/x-csv'
            self.response.out.write(problemSet.csvSet[coId-1])

class ProblemVisualPage(HtmlViewBase):
    path = '/problem([1-9][0-9]*)/problem([1-9][0-9]*)\\.html'
    @staticmethod
    def getPath(problemId, coId):
        return '/problem' + str(problemId) + '/problem' + str(coId) + '.html'
    def get(self, problemIdStr, coIdStr):
        problemId = int(problemIdStr)
        coId = int(coIdStr)
        problemSet = ProblemSet.Get(problemId)
        if problemSet!=None and 1<=coId and coId<=problemSet.getProblemCsvCount():
            problemCsv = problemSet.csvSet[coId-1]
            map = Trans.CsvToProblem(problemCsv)
            alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
            self.printHeader(u'問題%s - %s' % (alpha[coId-1], problemSet.subject), [TopPage.getMenu(), ProblemIndexPage.getMenu(problemId)])
            self.response.out.write('<p>')
            self.response.out.write(map.to_html())
            self.response.out.write('</p>')
            self.printFooter()
        else:
            self.response.headers['Content-Type'] = 'text/plain'
            self.response.out.write(u"そんな問題、無いっすよ。\n")

class GameLobbyPage(HtmlViewBase):
    path = '/problem([1-9][0-9]*)/start'
    @staticmethod
    def getPath(problemId):
        return '/problem' + str(problemId) + '/start'
    @login_required
    def get(self, problemIdStr):
        user = User.getNowUser()
        if user!=None:
            problemId = int(problemIdStr)
            problemSet = ProblemSet.Get(problemId)
            gameId = GameSession.New(user, problemSet)
            self.printHeader(u'待合室 - ' + problemSet.subject, [TopPage.getMenu()])
            self.response.out.write('<p>')
            self.response.out.write(u'<div>問題csvのURLは以下だけど、試合開始するまではダウンロードできないよ！</div>')
            self.response.out.write('</p>')
            self.response.out.write('<p>')
            for i in range(problemSet.getProblemCsvCount()):
                coId = i + 1
                alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                url = GameCsvViewer.getFullPath(gameId, coId)
                self.response.out.write(u'<div>問題%s　<a href="%s">%s</a></div>' % (alpha[i], url, url))
            self.response.out.write('</p>')
            self.response.out.write('<p>')
            self.response.out.write(u'<div>[[[ <a href="%s">試合開始!</a> ]]]</div>' % (GamePage.getPath(gameId)))
            self.response.out.write('</p>')
            self.printFooter()
        else:
            EntityCounter.inc("Error_login_required", default = 0)
            self.printHeader(u'待合室入室失敗 - ' + problemSet.subject, [TopPage.getMenu()])
            self.response.out.write(
u'''<pre>
たぶんGoogle側の不具合だと思っているのですが、
せっかくログインしてもらったのに、こちらにログイン情報が届いていません。
（届くのに、０．１～０．５秒ぐらい時間差があるのだと思っています）

お手数ですが、リロードしてみるか、いったんトップに戻って、もう一度このページに移動してみてください。
</pre>
'''
                )
            self.printFooter()

class GamePage(HtmlViewBase):
    path = '/booth([1-9][0-9]*)/'
    @staticmethod
    def getPath(gameId):
        return '/booth' + str(gameId) + '/'
    @classmethod
    def getMenu(cls, gameId):
        return (u'試合TOP', cls.getPath(gameId))
    @login_required
    def get(self, gameIdStr):
        gameId = int(gameIdStr)
        game = GameSession.GetNowUsersGame(gameId)
        if game!=None:
            game.startGame()
            problemSet = game.problemSet
            self.printHeader(u'試合 - ' + problemSet.subject, [TopPage.getMenu()])
            self.response.out.write('<p>')
            self.response.out.write(u'<div>問題csvのURLは以下。試合開始したのでダウンロードできるよ！</div>')
            self.response.out.write('</p>')
            self.response.out.write('<p>')
            for i in range(problemSet.getProblemCsvCount()):
                coId = i + 1
                alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                url = GameCsvViewer.getFullPath(gameId, coId)
                self.response.out.write(u'<div>問題%s　<a href="%s">%s</a></div>' % (alpha[i], url, url))
            self.response.out.write('</p>')
            self.response.out.write('<p>')
            self.response.out.write(u'<div>経過時間 <tt id="timer"></tt></div>')
            self.response.out.write('</p>')
            self.response.out.write('<p><table>')
            for i in range(problemSet.getProblemCsvCount()):
                coId = i + 1
                alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                self.response.out.write(
u'''<form action="%s" enctype="multipart/form-data" method="POST" target="post%s">
<tr>
<td valign="middle">問題%s <input name="csv_file" type="file" /> <input type="submit" value="問題%s 送信" /></td>
<td valign="middle"><iframe name="post%s" src="%s" height="20" vspace="0" marginheight="0" border="0" frameborder="0" scrolling="no"></iframe></td>
</tr>
</form>
'''
                    % (GameAnswerPost.getPath(gameId, coId), alpha[i], alpha[i], alpha[i], alpha[i], GameAnswerIFrame.getPath(gameId, coId)))
            self.response.out.write(
u'''</table></p>
<p><form action="%s" method="GET">
<input type="submit" value="試合を終える！" />
</form></p>
'''
                    % (GameFinishPage.getPath(gameId)))
            self.response.out.write(
u'''<hr>
<p>
<div><font color="#A00000"><b>カンニング機能（おまけ）</b></font></div>
'''
                )
            for i in range(problemSet.getProblemCsvCount()):
                coId = i + 1
                alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                url = GameCsvViewer.getFullPath(gameId, coId)
                self.response.out.write(u'<div>問題%s　[<a href="%s" target="_blank">問題</a>] [<a href="%s" target="_blank">回答</a>] [<a href="%s">人力</a>]</div>' % (alpha[i], GameVisualPage.getProblemPath(gameId, coId), GameVisualPage.getAnswerPath(gameId, coId), GameHumanModePage.getPath(gameId, coId)))
            self.response.out.write('</p>')
            dt = datetime.now() - game.startDate
            self.response.out.write(
'''<script language="javascript" type="text/javascript" src="/jquery-1.3.2.min.js"></script>
<script language="javascript" type="text/javascript">
jQuery(function(){
    var st = new Date().getTime() - %d;
    function updateTime(){
        var now = Math.floor((new Date().getTime() - st) / 1000.0);
        var min = Math.floor(now / 60);
        var sec = now - min * 60;
        if(sec<10)
        {
            sec = "0" + sec;
        }
        var hour = Math.floor(min / 60);
        if(hour)
        {
            min -= hour * 60;
            if(min<10)
            {
                min = "0" + min
            }
            if(hour<10)
            {
                hour = "&nbsp;&nbsp;" + hour;
            }
            else if(hour<100)
            {
                hour = "&nbsp;" + hour;
            }
            now = hour + ":" + min + ":" + sec;
        }
        else
        {
            if(min<10)
            {
                min = "&nbsp;" + min
            }
            now = "&nbsp;&nbsp;&nbsp;&nbsp;" + min + ":" + sec;
        }
        jQuery("#timer").html(now);
    }
    setInterval(updateTime,1000);
    updateTime();
});
</script>
'''
                % (dt.days * 86400000 + dt.seconds * 1000 + 500))
            self.printFooter()
    pass

class GameAnswerIFrame(webapp.RequestHandler):
    path = '/booth([1-9][0-9]*)/get_ans([1-9][0-9]*)'
    @staticmethod
    def getPath(gameId, coId):
        return '/booth%d/get_ans%d' % (gameId, coId)
    def get(self, gameIdStr, coIdStr):
        gameId = int(gameIdStr)
        coId = int(coIdStr)
        game = GameSession.GetNowUsersGame(gameId)
        if game!=None and 1<=coId and coId<=game.getProblemCsvCount():
            if game.csvSet[coId-1]!='':
                self.response.headers['Content-Type'] = 'text/plain'
                self.response.out.write(u"accepted\n")
            else:
                self.response.headers['Content-Type'] = 'text/plain'
                self.response.out.write(u"not accepted\n")

class GameAnswerPost(webapp.RequestHandler):
    path = '/booth([1-9][0-9]*)/post_ans([1-9][0-9]*)'
    @staticmethod
    def getPath(gameId, coId):
        return '/booth%d/post_ans%d' % (gameId, coId)
    def post(self, gameIdStr, coIdStr):
        gameId = int(gameIdStr)
        coId = int(coIdStr)
        game = GameSession.GetNowUsersGame(gameId)
        if game!=None and game.startDate!=None and game.finishDate==None and 1<=coId and coId<=game.getProblemCsvCount():
            csv_file = self.request.get('csv_file')
            problem_csv = game.problemSet.csvSet[coId-1]
            problem = Trans.CsvToProblem(problem_csv)
            try:
                answer = Trans.CsvToAnswer(csv_file)
                try:
                    answer.action(problem)
                    self.redirect(GameAnswerIFrame.getPath(gameId, coId))
                except ValueError:
                    csv_file = ''
                    self.response.headers['Content-Type'] = 'text/plain'
                    self.response.out.write(u"error: csv operation\n")
            except ValueError:
                csv_file = ''
                self.response.headers['Content-Type'] = 'text/plain'
                self.response.out.write(u"error: csv format\n")
            game.csvSet[coId-1] = csv_file
            game.ansTimeSet[coId-1] = datetime.now()
            game.put()

class GameHumanModeAnswerPost(webapp.RequestHandler):
    path = '/booth([1-9][0-9]*)/post_ans([1-9][0-9]*)_human'
    @staticmethod
    def getPath(gameId, coId):
        return '/booth%d/post_ans%d_human' % (gameId, coId)
    def post(self, gameIdStr, coIdStr):
        gameId = int(gameIdStr)
        coId = int(coIdStr)
        game = GameSession.GetNowUsersGame(gameId)
        if game!=None and game.startDate!=None and game.finishDate==None and 1<=coId and coId<=game.getProblemCsvCount():
            csv_data = self.request.get('csv_data')
            csv_data = csv_data.replace("\r\n", "\n").replace("\r", "\n").replace("\n", "\r\n")
            problem_csv = game.problemSet.csvSet[coId-1]
            problem = Trans.CsvToProblem(problem_csv)
            try:
                answer = Trans.CsvToAnswer(csv_data)
                try:
                    answer.action(problem)
                    self.redirect(GamePage.getPath(gameId))
                except ValueError:
                    csv_data = ''
                    self.response.headers['Content-Type'] = 'text/plain'
                    self.response.out.write(u"error: csv operation\n")
            except ValueError:
                csv_data = ''
                self.response.headers['Content-Type'] = 'text/plain'
                self.response.out.write(u"error: csv format\n")
            game.csvSet[coId-1] = csv_data
            game.ansTimeSet[coId-1] = datetime.now()
            game.put()

class GameFinishPage(HtmlViewBase):
    path = '/booth([1-9][0-9]*)/finish'
    @staticmethod
    def getPath(gameId):
        return '/booth%d/finish' % (gameId)
    @login_required
    def get(self, gameIdStr):
        gameId = int(gameIdStr)
        game = GameSession.GetNowUsersGame(gameId)
        if game!=None:
            game.finishGame()
            problemSet = game.problemSet
            entry = RankingEntry.GetNowUsersEntry(game.user, problemSet.problemId)
            self.printHeader(u'試合終了 - ' + problemSet.subject, [TopPage.getMenu()])
            self.response.out.write('<p><table>')
            if entry!=None:
                self.response.out.write(u'<tr><td></td><td align="center" colspan="3">今回</td><td>/</td><td align="center" colspan="3">記録</td></tr>')
            for i in range(game.getProblemCsvCount()):
                alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                score = game.calcScore(i)
                time = game.calcTime(i)
                self.response.out.write(u'<tr><td>問題%s：</td><td align="right">%d点 (</td><td align="right">%d手)</td><td align="right">%s</td>' % (alpha[i], score.point, score.opeCount, self.secToTimeStr(time)))
                if entry!=None:
                    self.response.out.write(u'<td>/</td><td align="right">%d点 (</td><td align="right">%d手)</td><td align="right">%s</td>' % (entry.scoreList[i], entry.opeCountList[i], self.secToTimeStr(entry.secList[i])))
                self.response.out.write('</tr>')
            self.response.out.write('</table></p>')

            self.response.out.write(u'<p>以上が、今回のあなたの成績です。</p>')
            if entry!=None:
                self.response.out.write(u'<p>前の記録を消して、今回の結果を登録しますか？</p>')
            else:
                self.response.out.write(u'<p>今回の結果を登録しますか？</p>')
            self.response.out.write(
u'''
<p><form action="%s" method="GET">
<input type="submit" value="登録する！" /> [<a href="%s">登録しない</a>]
</form></p>
'''
                    % (GameRegistPage.getPath(gameId), ProblemIndexPage.getPath(problemSet.problemId)))
            self.printFooter()

class GameRegistPage(HtmlViewBase):
    path = '/booth([1-9][0-9]*)/regist'
    @staticmethod
    def getPath(gameId):
        return '/booth%d/regist' % (gameId)
    @login_required
    def get(self, gameIdStr):
        gameId = int(gameIdStr)
        game = GameSession.GetNowUsersGame(gameId)
        if game!=None:
            RankingEntry.Regist(game)
            self.redirect(ProblemIndexPage.getPath(game.problemSet.problemId))

class GameCsvViewer(webapp.RequestHandler):
    path = '/game([1-9][0-9]*)-([1-9][0-9]*)\\.csv'
    @staticmethod
    def getFullPath(gameId, coId):
        return 'http://' + os.environ["HTTP_HOST"] + '/game' + str(gameId) + '-' + str(coId) + '.csv'
    @staticmethod
    def getPath(gameId, coId):
        return '/game' + str(gameId) + '-' + str(coId) + '.csv'
    def get(self, gameIdStr, coIdStr):
        gameId = int(gameIdStr)
        coId = int(coIdStr)
        game = GameSession.GetGame(gameId)
        if game!=None and 1<=coId and coId<=game.getProblemCsvCount():
            if game.startDate!=None:
                self.response.headers['Content-Type'] = 'application/x-csv'
                self.response.out.write(game.problemSet.csvSet[coId-1])
            else:
                self.response.headers['Content-Type'] = 'text/plain'
                self.response.out.write(u"まだゲーム始まってないよ。\n")
                self.response.out.write(u"本番だと、どんなレスポンスになるか分からんよ。\n")
                self.response.out.write(u"403とかだとエラーの区別がついて良いんだけどね。\n")
        else:
            self.response.headers['Content-Type'] = 'text/plain'
            self.response.out.write(u"そんなゲーム、無いっすよ。\n")

class GameVisualPage(HtmlViewBase):
    path = '/booth([1-9][0-9]*)/(problem|answer)([1-9][0-9]*)\\.html'
    @staticmethod
    def getProblemPath(gameId, coId):
        return '/booth' + str(gameId) + '/problem' + str(coId) + '.html'
    @staticmethod
    def getAnswerPath(gameId, coId):
        return '/booth' + str(gameId) + '/answer' + str(coId) + '.html'
    @login_required
    def get(self, gameIdStr, modeStr, coIdStr):
        gameId = int(gameIdStr)
        coId = int(coIdStr)
        game = GameSession.GetNowUsersGame(gameId)
        if game!=None and 1<=coId and coId<=game.getProblemCsvCount():
            problemCsv = game.problemSet.csvSet[coId-1]
            map = Trans.CsvToProblem(game.problemSet.csvSet[coId-1])
            if modeStr=='answer':
                answerCsv = game.csvSet[coId-1]
                if answerCsv!='':
                    ans = Trans.CsvToAnswer(answerCsv)
                    ans.action(map)
            self.printHeader(u'閲覧', [])
            self.response.out.write('<p>')
            self.response.out.write(map.to_html())
            self.response.out.write('</p>')
            self.printFooter()
        else:
            self.response.headers['Content-Type'] = 'text/plain'
            self.response.out.write(u"そんなゲーム、無いっすよ。\n")

class GameHumanModePage(HtmlViewBase):
    path = '/booth([1-9][0-9]*)/human([1-9][0-9]*)\\.html'
    @staticmethod
    def getPath(gameId, coId):
        return '/booth' + str(gameId) + '/human' + str(coId) + '.html'
    @login_required
    def get(self, gameIdStr, coIdStr):
        gameId = int(gameIdStr)
        coId = int(coIdStr)
        game = GameSession.GetNowUsersGame(gameId)
        if game!=None and 1<=coId and coId<=game.getProblemCsvCount():
            problemCsv = game.problemSet.csvSet[coId-1]
            map = Trans.CsvToProblem(game.problemSet.csvSet[coId-1])
            alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
            self.printHeader(u'人力モード - ' + game.problemSet.subject + u' - 問' + alpha[coId-1], [TopPage.getMenu(), GamePage.getMenu(gameId)])
            self.response.out.write(
u'''
<script language="javascript" type="text/javascript" src="/jquery-1.3.2.min.js"></script>
<script language="javascript" type="text/javascript" src="/pc2009.board.js"></script>
<script language="javascript" type="text/javascript">
jQuery(function(){
    var form = jQuery('<form method="POST" action="%s"></form>')
    var memo = pc2009.newMemo();
    memo.attr("name", "csv_data");
    form.append(jQuery('<div></div>').append(memo));
    form.append('<div><input type="submit" value="送信" /></div>')

    var board = pc2009.newBoard(%s, function(x, y, r){
        memo.regist(x, y, r);
    });
    var size_setting = pc2009.newSizeSetting(function(r){
        board.setR(r);
    });
    var button_bar = pc2009.newButtonBar([
        ['/clear.png', function(){
            board.clear();
            memo.clear();
        }],
        ['/undo.png', function(){
            board.undo(memo.undo());
        }]
    ]);
    jQuery('#main').append(board)
        .append(size_setting)
        .append(button_bar)
        .append(form);
});
</script>
<div id="main"></div>
'''
                    % (GameHumanModeAnswerPost.getPath(gameId, coId), map.to_json()))
            self.printFooter()
        else:
            self.response.headers['Content-Type'] = 'text/plain'
            self.response.out.write(u"そんなゲーム、無いっすよ。\n")

application = webapp.WSGIApplication([
        (TopPage.path, TopPage),
        (HelpPage.path, HelpPage),
        (MakeProblemPage.path, MakeProblemPage),
        (RandomMakeProblemPost.path, RandomMakeProblemPost),
        (FileMakeProblemPost.path, FileMakeProblemPost),
        (ProfilePage.path, ProfilePage),
        (ProfilePost.path, ProfilePost),
        (ProblemIndexPage.path, ProblemIndexPage),
        (ProblemCommentsViewPage.path, ProblemCommentsViewPage),
        (ProblemCommentPost.path, ProblemCommentPost),
        (ProblemCsvViewer.path, ProblemCsvViewer),
        (ProblemVisualPage.path, ProblemVisualPage),
        (GameLobbyPage.path, GameLobbyPage),
        (GamePage.path, GamePage),
        (GameAnswerIFrame.path, GameAnswerIFrame),
        (GameAnswerPost.path, GameAnswerPost),
        (GameCsvViewer.path, GameCsvViewer),
        (GameVisualPage.path, GameVisualPage),
        (GameHumanModePage.path, GameHumanModePage),
        (GameHumanModeAnswerPost.path, GameHumanModeAnswerPost),
        (GameFinishPage.path, GameFinishPage),
        (GameRegistPage.path, GameRegistPage)
    ], debug = True)

def main():
    run_wsgi_app(application)

if __name__ == '__main__':
    main()
